/*
******************************************************************************************************* 
**  Copyright (C) 2019, 苏州检易生物科技有限公司 
**  All rights reserved. 
** 
**  FileName:       	eth_task.c
**  Description:	
**  Author:        	 
**  Version				v0.1
**  Date:           	2019-05-09
**	Function List： 
**  History:         
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                              	调试开关
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include "eth_task.h"
#include "app_msg.h"
#include "eth.h"
#include "pbuf.h"
#include "os_config.h"
#include "tftpserver.h"
//#include "tcp_protocol.h"
#include "netif/etharp.h"
#include "dhcp.h"
#include "udp_server.h"
#include "tcp_client.h"
#include "debug_task.h"
/*
*********************************************************************************************************
*                                              	宏定义
*********************************************************************************************************
*/
#define		ETH_TASK_STK_SIZE			1024
#define		ETH_TASK_MB_SIZE			32
/*
*********************************************************************************************************
*                                              	结构体定义
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                              	函数原型声明
*********************************************************************************************************
*/

static void eth_task_process(void *p_arg);
static void LwIP_DHCP_Process_Handle(uint32_t time);
static void eth_event_callback(void* arg,eth_msg_t msg,struct pbuf *p);
static void udp_recv_msg(struct pbuf *p);
/*
*********************************************************************************************************
*                                              	全局变量定义
*********************************************************************************************************
*/

eth_cb_t	eth_handle = {0};
extern __IO uint64_t LocalTime;
/*
*********************************************************************************************************
*                                              	模块静态变量定义
*********************************************************************************************************
*/
/*模块初始化静态变量*/
static int mcu_id[3];
static uint64_t localtime = 0;
struct rt_thread   	rt_eth_tcb;
struct rt_mailbox		rt_eth_mb;
static char					eth_task_stk[ETH_TASK_STK_SIZE];
static char					eth_task_name[]="eth task\0";
static char					eth_task_mb_pool[ETH_TASK_MB_SIZE * 4];

static struct rt_mempool 	rt_eth_mp;
static uint8_t __attribute__((aligned(4))) rt_eth_mp_buff[RT_MEMPOOL_BYTE_SIZE(ETH_RECV_MEM_NUM,sizeof(eth_buff_t))];

static char		host_broadcast[] = "This is upper computer:192.168.31.164::5000";
//static uint8_t eth_recv_buff[ETH_RECV_MEM_LENGTH];

/*
*********************************************************************************************************
*                                              	函数定义
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                               本地函数声明
*********************************************************************************************************
*/
static void tcpclient_recv_proc(void * mbuffer);
static void tcpclient_send_proc(void * mbuffer);
static void eth_event_proc(uint32_t arg);
static void udp_log_output(uint8_t* buff,uint16_t size);
/*
*********************************************************************************************************
*@brief:						
*********************************************************************************************************
*/

void eth_task_start(void)
{
	if(RT_EOK != rt_thread_init(&rt_eth_tcb,
		eth_task_name, 
		eth_task_process,
		RT_NULL,
		eth_task_stk,
		ETH_TASK_STK_SIZE,
		ETH_TASK_PRIO,
		RT_THREAD_TIME_SLICE))
	{
		Critical_Log("%s create failed!\r\n",eth_task_name);
//		while(1){}
	}
	else
		rt_thread_startup(&rt_eth_tcb);
}
void eth_tcp_cb_reg(void (*pfn_cb)(eth_msg_t msg,uint8_t* buff,uint16_t size))
{
	eth_handle.tcp_protocol_callback = pfn_cb;
}
static void eth_task_process(void *arg)
{
	app_msg_t *p_msg = NULL;
	rt_err_t err;
  struct ip_addr netmask;
  struct ip_addr gw;

	mcu_id[0] = *((uint32_t*)STM32H7_MCU_ID_ADDR);
	mcu_id[1] = *((uint32_t*)(STM32H7_MCU_ID_ADDR+4));
	mcu_id[2] = *((uint32_t*)(STM32H7_MCU_ID_ADDR+8));
	
	eth_open();
	LwIP_Init();
	/*创建消息邮箱*/
	if(RT_EOK != rt_mb_init(&rt_eth_mb,
		"eth mailbox",
		eth_task_mb_pool,
		ETH_TASK_MB_SIZE,
		RT_IPC_FLAG_FIFO))
	{
		Critical_Log("%s create failed!\r\n",eth_task_name);
	}
	eth_handle.local_tcp_port = LOCAL_TCP_PORT;
	eth_handle.remote_tcp_port = DEST_TCP_PORT;
	eth_handle.local_udp_port  = CONSOLE_UDP_PORT_LOCAL;
	eth_handle.remote_udp_port = CONSOLE_UDP_PORT_REMOTE;
	eth_handle.eth_link_status = ETH_LINK_UP;
	IP4_ADDR(&eth_handle.remote_ip_addr,192,168,0,200);
	eth_handle.remote_tcp_port = 5001;
	IP4_ADDR(&eth_handle.local_ip_addr,192,168,0,35);
//	if(1)//SYSTEM_CFG->dhcp_en == 1)
//	{
//		eth_handle.dhcp_status = DHCP_START;
//		eth_handle.tcp_status = TCP_WAIT_HOST;
//	}
//	else
//	{
//		eth_handle.dhcp_status = DHCP_TIMEOUT;
//		app_msg_send(MSG_ETH_EVENT,DHCP_TIMEOUT,&rt_eth_mb);
//	}
	
	eth_handle.dhcp_status = DHCP_TIMEOUT;
	
	if(RT_EOK != rt_mp_init(&rt_eth_mp,
		"eth mem pool",
		rt_eth_mp_buff,
		RT_MEMPOOL_BYTE_SIZE(ETH_RECV_MEM_NUM,sizeof(eth_buff_t)),
		sizeof(eth_buff_t)
		))
	{
		Critical_Log("Eth recv mem create failed!\r\n");
	}
	eth_handle.timer = rt_timer_create("can timer",LwIP_Periodic_Handle,RT_NULL,ETH_PERIODIIC_INTERVAL,RT_TIMER_FLAG_PERIODIC);
	if(eth_handle.timer == NULL)
	{
		Critical_Log("%s create timer failed!\r\n",eth_task_name);
	}
	else
		rt_timer_start(eth_handle.timer);
	//	bsp_timer_callback_register(&TIM_1K,LwIP_Periodic_Handle);
//	log_tx_cb_reg(udp_log_output);
	app_msg_send(MSG_ETH_EVENT,UDP_START,&rt_eth_mb);
//	app_msg_send(MSG_ETH_EVENT,TCP_START,&rt_eth_mb);
	
	while(1)
	{
		if(RT_EOK != rt_mb_recv(&rt_eth_mb,(rt_uint32_t*)&p_msg,RT_WAITING_FOREVER)
			|| p_msg == NULL)
		{
			p_msg = NULL;
			continue;
		}
		
		switch(p_msg->event)
		{
			case MSG_TCPCLIENT_RECV:
				tcpclient_recv_proc((void*)(p_msg->data));
				break;
			case MSG_TCPCLIENT_SEND:
				tcpclient_send_proc((void*)(p_msg->data));
				break;
			case MSG_ETH_EVENT:
				eth_event_proc(p_msg->data);
				break;
			case MSG_UDP_LOG_OUTPUT:
				if(eth_handle.remote_ip_addr.addr != 0 && eth_handle.eth_link_status == ETH_LINK_UP)
				{
					udp_server_send(eth_handle.p_udp,eth_handle.remote_ip_addr,eth_handle.remote_udp_port,
						((eth_buff_t*)p_msg->data)->buff,((eth_buff_t*)p_msg->data)->length);
				}
				rt_mp_free(((eth_buff_t*)p_msg->data));
				break;
			default:
				break;
		}
		app_msg_free(p_msg);
	}
}
/*线程级网络首发消息处理函数*/
static void tcpclient_recv_proc(void * eth_buf)
{
	ASSERT(eth_buf);
	if(NULL != eth_handle.tcp_protocol_callback)
		eth_handle.tcp_protocol_callback(TCP_RECV,((eth_buff_t*)eth_buf)->buff,((eth_buff_t*)eth_buf)->length);
	memset(eth_buf,0,sizeof(eth_buff_t));
	rt_mp_free(eth_buf);
} 
static void tcpclient_send_proc(void * eth_buff)
{
	if(eth_handle.tcp_status == TCP_CONNECTED)
		tcp_client_send(eth_handle.p_tcp_client,((eth_buff_t*)eth_buff)->buff,((eth_buff_t*)eth_buff)->length);
	memset(eth_buff,0,sizeof(eth_buff_t));
	rt_mp_free(eth_buff);
}
/*线程级网络状态处理函数*/
static void eth_event_proc(uint32_t arg)
{
	
	rt_base_t it = rt_hw_interrupt_disable();
	uint32_t IPaddress;
	struct ip_addr netmask;
	struct ip_addr gw;
	switch(arg)
	{
		case ETH_LINK_DOWN:break;
		case ETH_LINK_UP:break;
		case DHCP_SUCCESS:
			Trace_Log("DHCP success, get local IP:%d:%d:%d:%d;\r\n",eth_handle.local_ip_addr.addr & 0xFF,
				(eth_handle.local_ip_addr.addr >> 8) & 0xFF,(eth_handle.local_ip_addr.addr >> 16) & 0xFF,
				(eth_handle.local_ip_addr.addr >> 24) & 0xFF);
			app_msg_send(MSG_ETH_EVENT,UDP_START,&rt_eth_mb);
			tftpd_init();
			break;
		case DHCP_TIMEOUT:
			Trace_Log("DHCP disable or timeout!\r\n");
			app_msg_send(MSG_ETH_EVENT,UDP_START,&rt_eth_mb);
			break;
		case UDP_START:
			tftpd_init();
			if(eth_handle.udp_status != UDP_START)
			{
				Trace_Log("DHCP conplete, start UDP broadcast to get remote server.\r\n");
				Trace_Log("Start UDP server, local port %d, remote port %d;\r\n",eth_handle.local_udp_port,eth_handle.remote_udp_port);
				eth_handle.udp_status = UDP_START;
				
//				if(eth_handle.dhcp_status != DHCP_SUCCESS)
//				{
//					IP4_ADDR(&eth_handle.local_ip_addr,IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3);
//					IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
//					IP4_ADDR(&gw,eth_handle.local_ip_addr.addr & 0xFF,
//					(eth_handle.local_ip_addr.addr >> 8) & 0xFF,(eth_handle.local_ip_addr.addr >> 16) & 0xFF,1);
//					netif_set_addr(&gnetif, &eth_handle.local_ip_addr , &netmask, &gw);
//				}
				eth_handle.p_udp = udp_server_start(CONSOLE_UDP_PORT_LOCAL,CONSOLE_UDP_PORT_REMOTE,eth_event_callback);
//				if(eth_handle.dhcp_status == DHCP_SUCCESS)
				{
					ip_set_option(eth_handle.p_udp, SOF_BROADCAST);
				}
				/*打印系统信息 IP信息*/
				system_message_print();
				TraceLog("Board IP 0x%08X;REMOTE IP 0x%08X;Local TCP port %d;Remote TCP port %d;\r\n"
				,eth_handle.local_ip_addr.addr,eth_handle.remote_ip_addr.addr,eth_handle.local_tcp_port,eth_handle.remote_tcp_port
				);
			}
			break;
		case UDP_GET_HOST_BROADCAST:
			/*在获取了服务器地址 如果未从DHCP获取地址 则生成一个本地IP*/
			if(eth_handle.dhcp_status != DHCP_SUCCESS)
			{
				tftpd_init();
				/*将remote ip 最低位加一为本地地址*/
				eth_handle.local_ip_addr.addr = eth_handle.remote_ip_addr.addr + 0x01000000;
				Trace_Log("Local IP undefined,set local ip:%d:%d:%d:%d;Port:%d;\r\n",eth_handle.local_ip_addr.addr & 0xFF,
				(eth_handle.local_ip_addr.addr >> 8) & 0xFF,(eth_handle.local_ip_addr.addr >> 16) & 0xFF,
				(eth_handle.local_ip_addr.addr >> 24) & 0xFF,eth_handle.local_tcp_port);
				IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
				IP4_ADDR(&gw,eth_handle.local_ip_addr.addr & 0xFF,
				(eth_handle.local_ip_addr.addr >> 8) & 0xFF,(eth_handle.local_ip_addr.addr >> 16) & 0xFF,1);
				netif_set_addr(&gnetif, &eth_handle.local_ip_addr , &netmask, &gw);
			}
			app_msg_send(MSG_ETH_EVENT,TCP_START,&rt_eth_mb);
			break;
		case TCP_START:
		//	eth_handle.local_ip_addr.addr = eth_handle.remote_ip_addr.addr + 0x01000000;
			IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
			IP4_ADDR(&gw,eth_handle.local_ip_addr.addr & 0xFF,
			(eth_handle.local_ip_addr.addr >> 8) & 0xFF,(eth_handle.local_ip_addr.addr >> 16) & 0xFF,1);
			netif_set_addr(&gnetif, &eth_handle.local_ip_addr , &netmask, &gw);
		
//			tftpd_init();
			Trace_Log("TCP connecting.\r\n");
			eth_handle.p_tcp_client = tcp_client_start(eth_handle.remote_ip_addr,eth_handle.remote_tcp_port,eth_event_callback);
			eth_handle.tcp_status = TCP_CONNECTING;
			eth_handle.tcp_timer = localtime;
			break;
		case TCP_CONNECTED:
			tcp_transfer_push(RPLY_ID,mcu_id,sizeof(mcu_id));
			eth_handle.tcp_status = TCP_CONNECTED;
			if(NULL != eth_handle.tcp_protocol_callback)
				eth_handle.tcp_protocol_callback(TCP_CONNECTED,NULL,0);
//			tcp_transfer_push(UPLD_PRODUCT_MSG,product_id,strlen(product_id));
			Trace_Log("TCP connected.\r\n");
			break;
		case TCP_DISCONNECT:/*TCP close不是立即完成的，再disconnect后要过一段时间再连接*/
			eth_handle.tcp_status = TCP_DISCONNECT;
			eth_handle.tcp_timer = localtime;
			if(NULL != eth_handle.tcp_protocol_callback)
				eth_handle.tcp_protocol_callback(TCP_DISCONNECT,NULL,0);
//			app_msg_send(MSG_ETH_EVENT,TCP_START,&rt_eth_mb);
			Trace_Log("TCP disconnect.\r\n");
			break;
	}
	rt_hw_interrupt_enable(it);
}
/*udp log 输出 在log输出中被调用，将log信息输出到eth线程*/
static void udp_log_output(uint8_t* buff,uint16_t size)
{
	ASSERT(buff);
	if(eth_handle.remote_ip_addr.addr == 0 || eth_handle.eth_link_status != ETH_LINK_UP)
		return;
	eth_buff_t *eth_buff = rt_mp_alloc(&rt_eth_mp,RT_WAITING_NO);
	if(NULL==eth_buff)
	{
		return;
	}
	eth_buff->length = size;
	memcpy(eth_buff->buff,buff,size);
	app_msg_send(MSG_UDP_LOG_OUTPUT, (uint32_t)eth_buff, &rt_eth_mb);
	return;
}
//在接收中断中被调用 将数据发送至任务级
static void eth_tcp_read(struct pbuf *p)
{
	ASSERT(p);
	if(p->tot_len > ETH_RECV_MEM_LENGTH)
	{
		return;
		Critical_Log("Eth recv msg length %d!\r\n",p->tot_len);
	}
	eth_buff_t *eth_buff = rt_mp_alloc(&rt_eth_mp,RT_WAITING_NO);
	if(NULL==eth_buff)
	{
		Critical_Log("Eth recv mem create failed!\r\n");
		return;
	}
	eth_buff->length = p->tot_len;
	
	uint16_t offset = 0;
	struct pbuf* p_next = p;
	do{
		memcpy(eth_buff->buff+offset,p_next->payload,p_next->len);
		offset+=p_next->len;
		p_next=p_next->next;
	}while(p_next!=NULL&&p_next->len!=0);
	app_msg_send(MSG_TCPCLIENT_RECV, (uint32_t)eth_buff, &rt_eth_mb);
	return;
}
//将需要发送的数据发送至任务级
void eth_tcp_write(uint8_t* buf, uint16_t size)
{
	if(eth_handle.tcp_status != TCP_CONNECTED)
		return;
	eth_buff_t *eth_buff = rt_mp_alloc(&rt_eth_mp,100);
	if(eth_buff == NULL)
	{
		Critical_Log("Eth send mem create failed!\r\n");
		return;
	}
	eth_buff->length = size;
	memcpy(eth_buff->buff,buf,size);
	if(0 != app_msg_send(MSG_TCPCLIENT_SEND, (uint32_t)eth_buff, &rt_eth_mb))
	{
		rt_mp_free(eth_buff);
		Critical_Log("TCP msg send failed!\n");
	}
	return;
}
/*网络连接相关周期性处理函数*/
void LwIP_Periodic_Handle(void* arg)
{
	rt_base_t it = rt_hw_interrupt_disable();
	localtime += ETH_PERIODIIC_INTERVAL;

	if (localtime  % LINK_TIMER_INTERVAL == 0) {
		uint32_t LinkStatus=LAN8720_GetLinkState();
		if(LinkStatus<=LAN8720_STATUS_LINK_DOWN && (gnetif.flags&NETIF_FLAG_LINK_UP)!=0)
		{
			eth_handle.eth_link_status = ETH_LINK_DOWN;
			netif_set_link_down(&gnetif);
			app_msg_send(MSG_ETH_EVENT,ETH_LINK_DOWN,&rt_eth_mb);
		}
		else if(LinkStatus>LAN8720_STATUS_LINK_DOWN && (gnetif.flags&NETIF_FLAG_LINK_UP)==0)
		{
			eth_handle.eth_link_status = ETH_LINK_UP;
			netif_set_link_up(&gnetif);
			app_msg_send(MSG_ETH_EVENT,ETH_LINK_UP,&rt_eth_mb);
		}
	}
	if(eth_handle.eth_link_status == ETH_LINK_DOWN)
		return;
  if (localtime % TCP_TMR_INTERVAL == 0)
  {
    tcp_tmr();
  }
  if (localtime % ARP_TMR_INTERVAL == 0)
  {
    etharp_tmr();
  }
	if(eth_handle.tcp_status == TCP_CONNECTING 
		&& localtime - eth_handle.tcp_timer > TCP_CONNECTING_TIMEOUT)
	{
		eth_handle.tcp_timer = localtime;
		tcp_disconnect(eth_handle.p_tcp_client);
	}
	if(eth_handle.tcp_status == TCP_DISCONNECT
		&& localtime - eth_handle.tcp_timer > TCP_RECONNECT_INTERVAL)
	{
		eth_handle.tcp_timer = localtime;
		app_msg_send(MSG_ETH_EVENT,TCP_START,&rt_eth_mb);
	}
	
	if(eth_handle.dhcp_status == DHCP_SUCCESS || eth_handle.dhcp_status == DHCP_TIMEOUT)
	{
	}
  else
	{
		if(localtime % DHCP_FINE_TIMER_MSECS == 0)
		{
			dhcp_fine_tmr();
			LwIP_DHCP_Process_Handle(localtime);
		}
  }
	if (localtime % DHCP_COARSE_TIMER_MSECS == 0
//		&& SYSTEM_CFG->dhcp_en
		&& eth_handle.dhcp_status == DHCP_SUCCESS)
	{
		dhcp_coarse_tmr();
	}
	rt_hw_interrupt_enable(it);
}
/*DHCP相关周期性处理函数 在os timer中调用*/
static void LwIP_DHCP_Process_Handle(uint32_t time)
{
  uint32_t IPaddress;
  switch (eth_handle.dhcp_status)
  {
		case DHCP_START:
    {
      eth_handle.dhcp_status = DHCP_WAITING;
      dhcp_start(&gnetif);
    }
			break;
		case DHCP_WAITING:
    {
      /* Read the new IP address */
      IPaddress = gnetif.ip_addr.addr;

      if (IPaddress!=0) 
      {
				eth_handle.dhcp_timer  =	time;
				eth_handle.dhcp_status = DHCP_SUCCESS;
        dhcp_stop(&gnetif);
				eth_handle.local_ip_addr = gnetif.ip_addr;
				app_msg_send(MSG_ETH_EVENT,DHCP_SUCCESS,&rt_eth_mb);
      }
      else
      {
        /* DHCP timeout */
        if (gnetif.dhcp->tries > 2)
        {
					eth_handle.dhcp_timer  =	time;
          eth_handle.dhcp_status = DHCP_TIMEOUT;
          dhcp_stop(&gnetif);
					app_msg_send(MSG_ETH_EVENT,DHCP_TIMEOUT,&rt_eth_mb);
        }
      }
    }
    break;
  default: break;
  }
}
/*在lwip的硬件接收中断中被调用*/
static void eth_event_callback(void* arg,eth_msg_t msg,struct pbuf *p)
{
	switch(msg)
	{
		case UDP_RECV:
		{
			udp_recv_msg(p);
			break;
		}
		case TCP_CONNECTED:
//			eth_handle.tcp_status = TCP_CONNECTED;
			app_msg_send(MSG_ETH_EVENT,TCP_CONNECTED,&rt_eth_mb);
			break;
		case TCP_DISCONNECT:
//			eth_handle.tcp_status = TCP_DISCONNECT;
			app_msg_send(MSG_ETH_EVENT,TCP_DISCONNECT,&rt_eth_mb);
			break;
		case TCP_RECV:
			eth_tcp_read(p);
			break;
		default:break;
	}
	pbuf_free(p);
}
//static char		host_broadcast[] = "This is upper computer:192.168.31.164::5000";
/*udp 接收处理广播*/
static void udp_recv_msg(struct pbuf *p)
{
	uint32_t ipaddr1;
	uint32_t ipaddr2;
	uint32_t ipaddr3;
	uint32_t ipaddr4;
	char* p_tmp;
	
	eth_buff_t *eth_buff = rt_mp_alloc(&rt_eth_mp,RT_WAITING_NO);
	if(NULL==eth_buff)
	{
		Critical_Log("Eth recv mem create failed!\r\n");
		return;
	}
	eth_buff->length = p->tot_len;
	uint16_t offset = 0;
	struct pbuf* p_next = p;
	do{
		memcpy(eth_buff->buff+offset,p_next->payload,p_next->len);
		offset+=p_next->len;
		p_next=p_next->next;
	}while(p_next!=NULL && p_next->len!=0);
//	eth_buff->buff[offset] = '\0';
//	offset++;
//	debug_cmd_input(eth_buff->buff,offset);
	
//	if(eth_handle.remote_ip_addr.addr == 0 && memcmp(eth_buff->buff,host_broadcast,sizeof("This is upper computer:") - 1) == 0)
//	{
//		p_tmp = eth_buff->buff + sizeof("This is upper computer:") - 1;
//		debug_get_dec(&p_tmp,&ipaddr1);
//		p_tmp++;
//		debug_get_dec(&p_tmp,&ipaddr2);
//		p_tmp++;
//		debug_get_dec(&p_tmp,&ipaddr3);
//		p_tmp++;
//		debug_get_dec(&p_tmp,&ipaddr4);
//		p_tmp += 2;
//		debug_get_dec(&p_tmp,&eth_handle.remote_tcp_port);
//		IP4_ADDR(&eth_handle.remote_ip_addr,ipaddr1,ipaddr2,ipaddr3,ipaddr4);
//		ip_reset_option(eth_handle.p_udp, SOF_BROADCAST);
//		Trace_Log("Get UDP broadcast, get remote IP:%d:%d:%d:%d;Port:%d;\r\n",
//			ipaddr1,ipaddr2,ipaddr3,ipaddr4,eth_handle.remote_tcp_port);
//		app_msg_send(MSG_ETH_EVENT,UDP_GET_HOST_BROADCAST,&rt_eth_mb);
//	}
//	
	rt_mp_free(eth_buff);
	return;
}